package tuvienna.jade.participant;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import tuvienna.jade.Constants;
import tuvienna.jade.InterestSet;
import tuvienna.jade.ParticipantStatus;

/**
 * An agent playing the role of a participant on a conference.
 * He can register to a table and deregister from it.
 *
 */
public class ParticipantAgent extends Agent{

	private static final long serialVersionUID = 5372742227560520037L;
	protected AID table = null;
	protected Map<AID, TableInformation> tables = new HashMap<AID,TableInformation>();
	protected InterestSet interests;
	protected String interestsString = null;
	protected int currentTime = 0; // working as an inner clock
	
	protected BigDecimal lastKnowledgeSum = BigDecimal.ZERO;
	
	DFAgentDescription[] evaluators = null;
	
	protected void setup() {

		final Behaviour findTableBehaviour = new FindTablesBehaviour(this);
		
		interests = new InterestSet();

		//Setup interests
		Object[] args = getArguments();
		if (args != null) 
		{ //add all arguments between -interests && the next parameter (starting with -)
			int start = 0;
			while(start < args.length && !"-interests".equals(args[start]))
				start ++;
			if(start < args.length && "-interests".equals(args[start]))
				while(++start < args.length && !args[start].toString().startsWith("-"))
					interests.put( args[start].toString(), new BigDecimal(0));

			//for backwards compatbility, no parameters(-) at all?
			//everything is an interest
			boolean hasParameters = false;
			for(int i=0;i<args.length;i++)
				if(args[i]!=null)
					hasParameters = hasParameters || (args[i]!=null && args[i].toString().startsWith("-"));

			if(!hasParameters)
				for(int i=0;i<args.length;i++)
					if(args[i]!=null)
						interests.put(args[i].toString(), new BigDecimal(0));
				
		}
			
		System.out.println("ParticipantAgent "+getLocalName()+" created. Interests: " + interests.toString());
		
		addBehaviour(new DiscussionParticipationBehavior(this));
		
		addBehaviour(new WakerBehaviour(this, 1000) {
			private static final long serialVersionUID = 1345643262434L;
			protected void handleElapsedTimeout() {
				addBehaviour(findTableBehaviour);
			}
		});
	}
	
	public InterestSet getInterests() {
		return this.interests;
	}
	
	public ParticipantAgent getAgent() {
		return this;
	}
	
	private DFAgentDescription[] getEvaluators() throws FIPAException {
		if (evaluators == null){
			DFAgentDescription template = new DFAgentDescription();
			ServiceDescription sd = new ServiceDescription();
			sd.setType(Constants.EVALUATOR_REGISTRATION_SERVICE);
			template.addServices(sd);
	
			evaluators = DFService.search(this, template);
		}
		return evaluators;
	}
	
	void setTable(AID table) {
		this.table = table;
		
		try { 
			//tell the evaluator, but find them all first
			
			for (int i = 0; i < getEvaluators().length; ++i) {
				ACLMessage msg  = new ACLMessage(ACLMessage.INFORM);
				msg.addReceiver(getEvaluators()[i].getName() );
				msg.setContentObject(new ParticipantStatus(this.interests, this.table));
				msg.setConversationId(Constants.EVALUATION);
				this.send(msg);
			}
		}	catch (Exception e) { 
			e.printStackTrace();
		}
	}
	
	
	void updateDiscussionTopic(InterestSet topic) {
		currentTime++;
		InterestSet relevantTopics = topic.filter(interests);
		interests.addSatisfaction(relevantTopics);
		TableInformation table = getTableInformation();
		if (table != null) {
			table.setLastGainedKnowledge(relevantTopics.sum(), currentTime, 
					table.getSize());
		}
	
	}
	
	public AID getTable() {
		return table;
	}

	void setTables(List<AID> tables) {
		for (AID table:tables) {
			this.tables.put(table, new TableInformation(table));
		}
	}
	
	protected TableInformation getTableInformation() {
		return this.tables.get(table);
	}
	
	private class FindTablesBehaviour extends OneShotBehaviour {
		private static final long serialVersionUID = -1974042780223679991L;
		private ParticipantAgent agent;
		
		public FindTablesBehaviour(ParticipantAgent agent) {
			super();
			this.agent = agent;
		}

		public void action() {
			DFAgentDescription template = new DFAgentDescription();
			ServiceDescription sd = new ServiceDescription();
			sd.setType(Constants.TABLE_REGISTRATION_SERVICE);
			template.addServices(sd);
			try {
				DFAgentDescription[] result = DFService.search(agent, template);
				List<AID> tables = new ArrayList<AID>(result.length);
				for (int i = 0; i < result.length; ++i) {
					tables.add(result[i].getName());
				}
				getAgent().setTables(tables);
			}	catch (FIPAException fe) {
				fe.printStackTrace();
			}
		}
		
	} // FindTablesBehaviour

}
